/*******************************************************************************
* Copyright (c) 2010 Eric Bodden.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eric Bodden - initial API and implementation
******************************************************************************/
package de.bodden.tamiflex.reporting;
import java.io.File;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.jar.JarFile;
import de.bodden.tamiflex.reporting.rt.ReflLogger;
import de.bodden.tamiflex.reporting.rt.ShutdownStatus;
public class Agent {
public final static String PKGNAME = Agent.class.getPackage().getName().replace('.', '/');
public static void premain(String agentArgs, Instrumentation inst) throws IOException, ClassNotFoundException, UnmodifiableClassException, URISyntaxException, InterruptedException {
if(!inst.isRetransformClassesSupported()) {
throw new RuntimeException("retransformation not supported");
}
if(agentArgs==null) agentArgs = "";
if(agentArgs.equals("")) usage();
appendRtJarToBootClassPath(inst);
String outPath=agentArgs;
if(outPath==null||outPath.isEmpty()) {
System.err.println("No outpath given!");
usage();
}
File outDir = new File(outPath);
if(outDir.exists()) {
if(!outDir.isDirectory()) {
System.err.println(outDir+ "is not a directory");
usage();
}
} else {
boolean res = outDir.mkdirs();
if(!res) {
System.err.println("Cannot create directory "+outDir);
usage();
}
}
final File logFile = new File(outDir,"refl.log");
ReflLogger.setLogFile(logFile);
instrumentClassesForLogging(inst);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
ShutdownStatus.hasShutDown = true;
ReflLogger.closeLogger();
}
});
}
private static void instrumentClassesForLogging(Instrumentation inst) throws UnmodifiableClassException {
ReflectionMonitor reflMonitor = new ReflectionMonitor();
inst.addTransformer(reflMonitor, true /* can retransform */);
//make sure that these classes are instrumented
inst.retransformClasses(Class.class,Method.class,Constructor.class);
//remove transformer again
inst.removeTransformer(reflMonitor);
}
private static void appendRtJarToBootClassPath(Instrumentation inst) throws URISyntaxException, IOException {
URL locationOfAgent = Agent.class.getResource("/de/bodden/tamiflex/reporting/rt/ReflLogger.class");
if(locationOfAgent==null) {
System.err.println("Support library for reflection log not found on classpath.");
System.exit(1);
}
agentJarFilePath = locationOfAgent.getPath().substring(0, locationOfAgent.getPath().indexOf("!"));
URI uri = new URI(agentJarFilePath);
JarFile jarFile = new JarFile(new File(uri));
inst.appendToBootstrapClassLoaderSearch(jarFile);
}
private static void usage() {
System.out.println("TamiFlex version "+Agent.class.getPackage().getImplementationVersion()+", Play-Out Agent \n");
System.out.println("This agent accepts the following options:");
System.out.println("[dontDumpClasses,][dontNormalize,][count,][verbose,]<path>");
System.out.println();
System.out.println("If 'dontDumpClasses' is given then the agent only produces a log file but dumps no classes.");
System.out.println("If 'dontNormalize' is given then the agent will not normalize randomized class names.");
System.out.println("If 'count' is selected then the agent will add the number of reflective invocations");
System.out.println("to the end of each line of the trace file.");
System.out.println("If 'verbose' is selected then the agent will print out all entries that it also added");
System.out.println("to the log file for the current run.");
System.out.println("");
System.out.println("The 'path' points to the output directory. The agent will write all class files");
System.out.println("into this directory. In addition, the agent will write a log file 'refl.log'. If this");
System.out.println("file already exists in the 'outpath' directory then the agent will add to the log,");
System.out.println("incrementing the respective counts, etc.");
System.out.println("");
System.out.println("");
System.out.println("For instance, the following command will cause the agent to dump class files into");
System.out.println("the directory /tmp/out, counting reflective invocations:");
System.out.println("java -javaagent:agent.jar=count,outpath=/tmp/out ...");
System.out.println(DISCLAIMER);
System.exit(1);
}
private final static String DISCLAIMER=
"Copyright (c) 2010 Eric Bodden.\n" +
"\n" +
"DISCLAIMER: USE OF THIS SOFTWARE IS AT OWN RISK.\n" +
"\n" +
"All rights reserved. This program and the accompanying materials\n" +
"are made available under the terms of the Eclipse Public License v1.0\n" +
"which accompanies this distribution, and is available at\n" +
"http://www.eclipse.org/legal/epl-v10.html";
private static String agentJarFilePath;
}